home *** CD-ROM | disk | FTP | other *** search
Wrap
/* ----------------------------------------------------------------------------- WarpSPELL ©1996 Dietmar Eilert Spell checking syntax parser. Dice: DMAKE ------------------------------------------------------------------------------- */ #include "defs.h" /// "Header stuff" // Buffer handles are allocated for each text buffer to keep track of ressources: struct BufferHandle { struct EditConfig *bh_EditConfig; // pointer to text data struct GlobalConfig *bh_GlobalConfig; // editor configuration struct SyntaxChunk *bh_SyntaxStack; // parser output struct RefreshRequest bh_RefreshRequest; // display refresh request UBYTE bh_Command[4096]; // ISpell command buffer UBYTE bh_Results[4096]; // ISpell result buffer struct MsgPort *bh_Port; // ISpell reply port }; // AutoConfig semaphore coordinates ISpell usage struct AutoConfig { struct SignalSemaphore Semaphore; UWORD Users; // ISpell users ULONG Words; // new words counter }; #define EMPTY_STACK ((struct SyntaxChunk *)~0) // empty stack flag /// /// "Prototype" // library functions Prototype LibCall struct ParserData *MountScanner(void); Prototype LibCall ULONG StartScanner(__A0 struct GlobalConfig *, __A1 struct EditConfig *, __D0 struct SyntaxChunk *); Prototype LibCall ULONG CloseScanner(__D0 ULONG); Prototype LibCall void FlushScanner(__D0 ULONG); Prototype LibCall void SetupScanner(__A0 struct GlobalConfig *); Prototype LibCall struct RefreshRequest *BriefScanner(__D0 ULONG, __A0 struct ScannerNotify *); Prototype LibCall struct SyntaxChunk *ParseLine (__D0 ULONG, __A0 struct LineNode *, __D1 ULONG); Prototype LibCall void UnparseLines(__A0 struct LineNode *, __D0 ULONG); Prototype LibCall void ParseSection(__D0 ULONG, __A0 struct LineNode *, __D1 ULONG); // private functions Prototype struct SyntaxChunk *ParseString(UBYTE *, UWORD, ULONG); Prototype struct SyntaxChunk *DupStack(struct SyntaxChunk *); Prototype ULONG *SendRexxCommand(UBYTE *, UBYTE *, struct MsgPort *replyPort, UBYTE *); Prototype BOOL CheckThisWord(ULONG, UBYTE *, UWORD); Prototype void AlertInfo(UBYTE *); Prototype struct Window *OpenMessageWin(UBYTE *, BOOL); Prototype struct Window *Message(UBYTE *, BOOL, struct Window *); Prototype struct MsgPort *RunISpell(void); Prototype BOOL ExistPath(UBYTE *); Prototype BOOL DoExecute(UBYTE *, BOOL, UBYTE *, UBYTE *, ULONG, WORD); Prototype struct MsgPort * RunISpell(void); Prototype struct MsgPort *WaitForISpell(void); Prototype void StopISpell(void); Prototype struct FileInfoBlock *FileInfo(UBYTE *, struct FileInfoBlock *); /// /// "Globals" struct SignalSemaphore ISpellSemaphore; BOOL IsLetter [256]; BOOL IsBoundary[256]; BOOL QuickCheck; /// /// "Library functions" /* ------------------------------- MountScanner -------------------------------- Called by the editor before first usage of a scanner. Return a description of our abilities. */ __geta4 LibCall struct ParserData * MountScanner() { static UBYTE version[] = "$VER: WarpSPELL 2.5 (" __COMMODORE_DATE__ ")"; static struct ParserData parserData; // syntax elements understood by parser static UBYTE *levelNames[] = { "Standard text", "Spelling error", NULL }; static UBYTE *example[] = { "Aaron ", "aback ", "abandon ", "abase ", "abash ", "abate ", "abatement ", "abbe ", "abbess ", "abbey ", "abbot ", "abbreviate ", "abdicate ", "abdominal ", "abduct ", "adenoids ", "already ", "alsatian ", "also ", "altar ", "alter ", NULL }; // color suggestions static ULONG levelColors[] = { MAKE_RGB4(0, 0, 0), MAKE_RGB4(15, 15, 0), }; parserData.pd_Release = SCANLIBVERSION; parserData.pd_Version = 1; parserData.pd_Serial = 0; parserData.pd_Info = "WarpSPELL 2.5"; parserData.pd_Levels = 2; parserData.pd_Names = levelNames; parserData.pd_Colors = levelColors; parserData.pd_Flags = SCPRF_SYNTAXCACHE; parserData.pd_Example = example; return(&parserData); } /* ------------------------------- StartScanner -------------------------------- Called by the editor after a new text buffer has been created. We allocate a buffer to hold text-specific data. The buffer address is returned as handle. */ LibCall ULONG StartScanner(__A0 struct GlobalConfig *globalConfigPtr, __A1 struct EditConfig *editConfigPtr, __D0 struct SyntaxChunk *syntaxStack) { struct BufferHandle *handle; if (handle = AllocVec(sizeof(struct BufferHandle), MEMF_PUBLIC | MEMF_CLEAR)) { if (handle->bh_Port = CreateMsgPort()) { handle->bh_GlobalConfig = globalConfigPtr; handle->bh_EditConfig = editConfigPtr; handle->bh_SyntaxStack = syntaxStack; if (RunISpell()) return((ULONG)handle); else { DeleteMsgPort(handle->bh_Port); FreeVec(handle); } } else FreeVec(handle); } return(NULL); } /* ------------------------------- CloseScanner -------------------------------- Called by the editor if a text buffer is about to be closed. Deallocate buffer specific 'global' data. */ LibCall ULONG CloseScanner(__D0 ULONG scanID) { if (scanID) { struct BufferHandle *handle = (struct BufferHandle *)scanID; if (handle->bh_Port) DeleteMsgPort(handle->bh_Port); StopISpell(); FreeVec(handle); } return(0); } /* ------------------------------- FlushScanner -------------------------------- Called by the editor in low memory situations: we are supposed to free as much memory as possible. */ LibCall void FlushScanner(__D0 ULONG scanID) { struct BufferHandle *handle; struct EditConfig *config; struct LineNode *lineNode; handle = (struct BufferHandle *)scanID; config = handle->bh_EditConfig; if (lineNode = config->TextNodes) UnparseLines(lineNode, config->Lines); } /* ------------------------------- SetupScanner -------------------------------- Called by the editor if the user wants to change the scanner's configuration. We do not support user configuration (parserData.pd_Flags: SCPRF_CONFIGWIN flag unset). */ LibCall void SetupScanner(__A0 globalConfigPtr) { ; } /* ------------------------------- BriefScanner -------------------------------- Called to notify a context scanner if lines have been added, deleted or modified. We aren't a context scanner (parserData.pd_Flags: SCPRF_CONTEXT flag unset), so we won't ever have to request additional display requests: the editor's built-in refresh of damage regions is sufficient. */ LibCall struct RefreshRequest * BriefScanner(__D0 ULONG scanID, __A0 struct ScannerNotify *notify) { return(NULL); } /* --------------------------------- ParseLine --------------------------------- Parse a line, build a syntax description */ LibCall struct SyntaxChunk * ParseLine(__D0 ULONG scanID, __A0 struct LineNode *lineNode, __D1 ULONG line) { if (IS_FOLD(lineNode)) return(NULL); else if (lineNode->Len) { // line not yet parsed ? if (lineNode->UserData == NULL) { struct SyntaxChunk *syntaxStack = ParseString(lineNode->Text, lineNode->Len, scanID); if (syntaxStack == EMPTY_STACK) lineNode->UserData = EMPTY_STACK; else lineNode->UserData = DupStack(syntaxStack); } if (lineNode->UserData == EMPTY_STACK) return((struct SyntaxChunk *)NULL); else return((struct SyntaxChunk *)lineNode->UserData); } else return(NULL); } /* -------------------------------- UnparseLines ------------------------------- Called by the editor if lines are to be deleted. We are supposed to free private data attached to the lines. */ LibCall void UnparseLines(__A0 struct LineNode *lineNode, __D0 ULONG lines) { while (lines--) { // free syntax cache if (lineNode->UserData) { if (lineNode->UserData != (APTR)EMPTY_STACK) FreeVec((APTR)lineNode->UserData); lineNode->UserData = NULL; } // free folded subblock if (IS_FOLD(lineNode)) { struct Fold *fold = (struct Fold *)lineNode->SpecialInfo; UnparseLines(fold->TextNodes, fold->Lines); } ++lineNode; } } /* -------------------------------- ParseSection ------------------------------- Called by the editor if lines are to be displayed. The scanner is encouraged to preparse the lines. */ LibCall void ParseSection(__D0 ULONG scanID, __A0 struct LineNode *lineNode, __D1 ULONG lines) { struct Window *win; ULONG done; UBYTE title[80]; if (lines > 100) win = Message("Checking text...", FALSE, NULL); else win = NULL; for (done = 0; done < lines; ++done) { if (win) { if ((done & 63) == 63) { sprintf(title, "Words (%d/%d)", done, lines); SetWindowTitles(win, title, (UBYTE *)~0); } } // fold headers have to be ignored if (IS_FOLD(lineNode) == FALSE) { // line not yet parsed ? if (lineNode->Len) if (lineNode->UserData == NULL) lineNode->UserData = DupStack(ParseString(lineNode->Text, lineNode->Len, scanID)); } ++lineNode; } if (win) CloseWindow(win); } /// /// "Private" /* -------------------------------- ParseString -------------------------------- Parse a string, build a syntax description. This is a simple example only: C++-Comments (// ....) are highlighted. Return EMPTY_STACK in case there is nothing to highlight. */ struct SyntaxChunk * ParseString(UBYTE *text, UWORD len, ULONG scanID) { if (len) { struct SyntaxChunk *syntaxStack = ((struct BufferHandle *)scanID)->bh_SyntaxStack; UWORD indent, elements, wordLen; elements = 0; // leading spaces have to be ignored for (indent = 0; len && (*text == 32); ++indent, --len) ++text; // trailing spaces have to be ignored while (len && (text[len - 1] == 32)) --len; // check every word of this line while (len) { // ignore spaces while (len && (IsLetter[*text] == FALSE)) { ++text; ++indent; --len; } if (len) { // get next word for (wordLen = 1; wordLen < len; ++wordLen) { UBYTE *check = text + wordLen; // end of word detected ? if (IsLetter[*check] == FALSE) { // verify end of word (boundary characters can be space OR letter) if (IsBoundary[*check]) { if ((wordLen + 1) < len) { if (IsLetter[*++check] == FALSE) break; } else break; } else break; } } // check word if (CheckThisWord(scanID, text, wordLen)) { // highlight word syntaxStack[elements].sc_Start = indent; syntaxStack[elements].sc_End = indent + (wordLen - 1); syntaxStack[elements].sc_Level = 1; ++elements; } // move to next word text += wordLen; indent += wordLen; len -= wordLen; } } if (elements) { // terminate syntax stack syntaxStack[elements].sc_Start = FALSE; syntaxStack[elements].sc_End = FALSE; syntaxStack[elements].sc_Level = FALSE; return(syntaxStack); } } return(EMPTY_STACK); } /* --------------------------------- DupStack ---------------------------------- Duplicate syntax stack (to be FreeVec'ed). Return NULL in case of failure. */ struct SyntaxChunk * DupStack(syntaxStack) struct SyntaxChunk *syntaxStack; { if (syntaxStack && (syntaxStack != EMPTY_STACK)) { struct SyntaxChunk *chunk; UWORD elements; // determine stack size for (elements = 0, chunk = syntaxStack; chunk->sc_Level; ++chunk) ++elements; // create copy of syntax stack (to be attached to a text line by the caller) if (elements) { ULONG size = (++elements) * sizeof(struct SyntaxChunk); chunk = syntaxStack; if (syntaxStack = AllocVec(size, MEMF_PUBLIC)) movmem(chunk, syntaxStack, size); } else syntaxStack = EMPTY_STACK; } return(syntaxStack); } /* ------------------------------- CheckThisWord ------------------------------- Check word <check> of length <len>. Return TRUE if word is not known. Ignore single letters. */ __geta4 BOOL CheckThisWord(scanID, check, len) UBYTE *check; UWORD len; ULONG scanID; { BOOL error = FALSE; if (len > 1) { struct BufferHandle *handle = (struct BufferHandle *)scanID; UBYTE *command = handle->bh_Command; movmem(check, command, len); command[len] = 0; strins(command, "QUICKCHECK "); // make ISpell check command quickly if (SendRexxCommand("IRexxSpell", command, handle->bh_Port, handle->bh_Results)) { // unknown word (not 'ok') ? if (stricmp(handle->bh_Results, "ok")) { if (QuickCheck) error = TRUE; else if (SendRexxCommand("IRexxSpell", command + 5, handle->bh_Port, handle->bh_Results)) { switch (*handle->bh_Results) { case '&': // spelling error, near misses available case '?': // spelling error, no near misses error = TRUE; break; case '*': // valid word case '+': // valid after affix removal case '-': // valid compound error = FALSE; break; default: error = FALSE; // unknown return code (assume that word is valid) } } } } } return(error); } /* ---------------------------------- SendRexxCommand ------------------------- Send ARexx message & wait for answer. Return pointer to result or NULL. */ __geta4 ULONG * SendRexxCommand(port, cmd, replyPort, buffer) struct MsgPort *replyPort; UBYTE *cmd, *port, *buffer; { struct MsgPort *rexxport; Forbid(); if (rexxport = FindPort(port)) { struct RexxMsg *rexxMsg, *answer; if (rexxMsg = CreateRexxMsg(replyPort, NULL, NULL)) { if (rexxMsg->rm_Args[0] = CreateArgstring(cmd, strlen(cmd))) { static ULONG result; rexxMsg->rm_Action = RXCOMM | RXFF_RESULT; PutMsg(rexxport, &rexxMsg->rm_Node); do { WaitPort(replyPort); if (answer = (struct RexxMsg *)GetMsg(replyPort)) result = answer->rm_Result1; } while (!answer); Permit(); if (answer->rm_Result1 == RC_OK) { if (answer->rm_Result2) { if (buffer) strcpy(buffer, (char *)answer->rm_Result2); DeleteArgstring((char *)answer->rm_Result2); } } DeleteArgstring((char *)ARG0(answer)); DeleteRexxMsg(answer); return(&result); } } } Permit(); return(NULL); } /* --------------------------------- AlertInfo ----------------------------------- Show alert. Returns TRUE if user pressed left button. */ __geta4 void AlertInfo(text) UBYTE *text; { UBYTE *buffer; if (buffer = AllocVec(80, MEMF_PUBLIC | MEMF_CLEAR)) { buffer[1] = '\30'; buffer[2] = '\25'; strcpy(buffer + 3, text); DisplayAlert(RECOVERY_ALERT, buffer, 40); FreeVec(buffer); } } /* -------------------------------- OpenMessageWin ----------------------------- Open info window */ struct Window * OpenMessageWin(text, sync) UBYTE *text; BOOL sync; { struct TextAttr textAttr = { "topaz.font", 8, 0, FPF_DESIGNED }; struct Window *win; struct Screen *screen; struct TextFont *font; WORD len, width, height, fontW, fontH, x, y, displayW, displayH, xOffset, yOffset; displayW = FALSE; displayH = FALSE; // critical stuff (barely legal - don't do this at home, kids ;-) Forbid(); if (screen = ((struct IntuitionBase *)IntuitionBase)->ActiveScreen) { struct Rectangle clip; if (QueryOverscan(GetVPModeID(&screen->ViewPort), &clip, OSCAN_TEXT)) { displayW = clip.MaxX - clip.MinX + 1; displayH = clip.MaxY - clip.MinY + 1; } xOffset = -screen->ViewPort.DxOffset; yOffset = -screen->ViewPort.DyOffset; textAttr = *screen->Font; } Permit(); if (font = OpenDiskFont(&textAttr)) { fontW = font->tf_XSize; fontH = font->tf_YSize; CloseFont(font); } else fontW = fontH = textAttr.ta_YSize; len = strlen(text); if (len < 30) len = 30; width = 20 + fontW * len; height = 20 + fontH; if (displayW) { x = ((displayW - width )>>1) + xOffset; y = ((displayH - height)>>1) + yOffset; } else x = y = 0; win = OpenWindowTags(NULL, WA_PubScreen, screen, WA_Left, x, WA_Top, y, WA_InnerWidth, width, WA_InnerHeight, height, WA_Title, "Words", WA_ScreenTitle, "Words", WA_Flags, WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_SMART_REFRESH | WFLG_ACTIVATE | WFLG_RMBTRAP, WA_GimmeZeroZero, TRUE, TAG_DONE ); if (win) { if (font) SetFont(win->RPort, font); win = Message(text, sync, win); } return(win); } /* ---------------------------------- Message ---------------------------------- Show message (assume that window is sufficiently sized) */ struct Window * Message(text, sync, win) struct Window *win; UBYTE *text; BOOL sync; { if (win == NULL) win = OpenMessageWin(text, sync); else { struct RastPort *rast = win->RPort; SetAPen(rast, 0); SetRast(rast, 0); SetAPen(rast, 1); Move(rast, 10, 10 + rast->TxBaseline); Text(rast, text, strlen(text)); if (sync) { Delay(150); CloseWindow(win); win = NULL; } } return(win); } /* --------------------------------- ExistPath ----------------------------------- Check wether file/directory exists. */ BOOL ExistPath(path) UBYTE *path; { BPTR lock; if (lock = Lock(path, ACCESS_READ)) UnLock(lock); if (lock) return(TRUE); else return(FALSE); } /* --------------------------------- DoExecute --------------------------------- Run DOS command */ BOOL DoExecute(cmd, async, output, directory, stack, prio) UBYTE *cmd, *output, *directory; ULONG stack; WORD prio; BOOL async; { BPTR newCurrentDir; BOOL success; success = FALSE; if (newCurrentDir = Lock(directory, SHARED_LOCK)) { BPTR inHandle, outHandle, oldCurrentDir; oldCurrentDir = CurrentDir(newCurrentDir); if (outHandle = Open(output, MODE_NEWFILE)) { struct MsgPort *oldct, *newct; if (IsInteractive(outHandle)) { newct = ((struct FileHandle *)BADDR(outHandle))->fh_Type; oldct = SetConsoleTask(newct); inHandle = Open("CONSOLE:", MODE_OLDFILE); SetConsoleTask(oldct); } else { newct = NULL; inHandle = Open("NIL:", MODE_OLDFILE); } if (inHandle) { struct TagItem tagItems[] = { SYS_Output, (ULONG)NULL, SYS_Input, (ULONG)NULL, NP_ConsoleTask, (ULONG)NULL, SYS_Asynch, (ULONG)FALSE, NP_StackSize, (ULONG)8196, NP_Priority, (ULONG)0, SYS_UserShell, (ULONG)TRUE, TAG_DONE }; tagItems[0].ti_Data = (ULONG)outHandle; tagItems[1].ti_Data = (ULONG)inHandle; tagItems[2].ti_Data = (ULONG)newct; tagItems[3].ti_Data = (ULONG)async; tagItems[4].ti_Data = (ULONG)stack; tagItems[5].ti_Data = (ULONG)prio; if (SystemTagList(cmd, tagItems) != -1L) success = TRUE; if (!(async && success)) Close(inHandle); } if (!(async && success)) Close(outHandle); } CurrentDir(oldCurrentDir); UnLock(newCurrentDir); } return(success); } /* --------------------------------- FileInfo ---------------------------------- Return file information or NULL (writes to <fib>) */ struct FileInfoBlock * FileInfo(name, fib) struct FileInfoBlock *fib; UBYTE *name; { BPTR handle; if (handle = Lock(name, ACCESS_READ)) { if (Examine(handle, fib)) { UnLock(handle); return(fib); } else UnLock(handle); } return(NULL); } /// /// "ISpell" /* --------------------------------- RunISpell --------------------------------- Run ISpell (if not yet running); increase user count */ struct MsgPort * RunISpell() { struct MsgPort *port; struct AutoConfig *autoConfig; // serialize access within this library ObtainSemaphore(&ISpellSemaphore); Forbid(); // ISpell running/starting already ? if (autoConfig = (struct AutoConfig *)FindSemaphore("WORDS")) { ObtainSemaphore(&autoConfig->Semaphore); ++autoConfig->Users; ReleaseSemaphore(&autoConfig->Semaphore); port = WaitForISpell(); if (port == NULL) Message("STARTUP FAILURE", TRUE, NULL); } else { port = FindPort("IRexxSpell"); // run ISpell on our own ? if (port == NULL) { struct AutoConfig *autoConfig; if (autoConfig = (struct AutoConfig *)AllocVec(sizeof(struct AutoConfig), MEMF_PUBLIC | MEMF_CLEAR)) { static UBYTE hashfile[255], language[255], personal[255], options[255], command[255], home[255]; __aligned struct FileInfoBlock fib = { 0 }; autoConfig->Semaphore.ss_Link.ln_Name = "WORDS"; autoConfig->Semaphore.ss_Link.ln_Pri = 0; autoConfig->Users = 1; AddSemaphore (&autoConfig->Semaphore); ObtainSemaphore(&autoConfig->Semaphore); // set default language strcpy(language, "english"); // set default options strcpy(options, "-W 1"); // did user provide special startup arguments for ISpell ? if (GetVar("WORDS.prefs", command, sizeof(command), GVF_GLOBAL_ONLY) != -1) { if (*command) { struct RDArgs *rdArgs, *args; if (rdArgs = AllocDosObject(DOS_RDARGS, NULL)) { ULONG argArray[] = { 0, 0, 0, 0, 0 }; // make LF-terminated copy of command string (required by dos/readArgs): strcat(command, "\12"); rdArgs->RDA_Source.CS_Buffer = command; rdArgs->RDA_Source.CS_Length = strlen(command); rdArgs->RDA_Source.CS_CurChr = 0; rdArgs->RDA_DAList = 0; rdArgs->RDA_Buffer = NULL; if (args = ReadArgs("LANGUAGE/K,QUICKCHECK/K,BOUNDARYCHARS/K,BEEP/N,ARGS/F", argArray, rdArgs)) { if (argArray[0]) // LANGUAGE/K strcpy(language, (UBYTE *)argArray[0]); if (argArray[4]) // ARGS/F strcpy(options, (UBYTE *)argArray[4]); FreeArgs(args); } else Message("Syntax error", TRUE, NULL); FreeDosObject(DOS_RDARGS, rdArgs); } } } // personal dictionary file (for new words) sprintf(personal, "/ispell/lib/.ispell_%s", language); // standard dictionary file sprintf(hashfile, "ispell:lib/%s.hash", language); // dictionary available ? if (FileInfo(hashfile, &fib)) { ULONG avail = AvailMem(MEMF_LARGEST); if (fib.fib_Size < avail) { struct Window *win = Message("Starting ISpell...", FALSE, NULL); // $HOME required by ISpell 3.1.18 if (GetVar("HOME", home, sizeof(home), GVF_GLOBAL_ONLY) == -1) SetVar("HOME", "sys:", -1, GVF_GLOBAL_ONLY); // run ISpell in host mode if (*options) sprintf(command, "ispell:bin/ispell -d%s -p%s -r %s", hashfile, personal, options); else sprintf(command, "ispell:bin/ispell -d%s -p%s -r", hashfile, personal); // wait for completion of ISpell startup if (DoExecute(command, TRUE, "NIL:", "ispell:", 32768, 1)) port = WaitForISpell(); if (port == NULL) Message("STARTUP FAILURE", TRUE, win); else if (win) CloseWindow(win); } else { if (fib.fib_Size < AvailMem(MEMF_ANY)) Message("Out of RAM (fragmentation error)", TRUE, NULL); else Message("Out of RAM", TRUE, NULL); } } else Message("Dictionary not found", TRUE, NULL); ReleaseSemaphore(&autoConfig->Semaphore); if (port == NULL) StopISpell(); } } } Permit(); ReleaseSemaphore(&ISpellSemaphore); return(port); } /* ------------------------------- WaitForISpell ------------------------------- Wait for ISpell */ struct MsgPort * WaitForISpell() { struct MsgPort *port; UWORD try; for (try = 50; try-- && (port == NULL); Delay(10)) { Forbid(); port = FindPort("IRexxSpell"); Permit(); } return(port); } /* -------------------------------- StopISpell --------------------------------- Decrement ISpell user count. Unload ISpell if unused. */ void StopISpell() { struct AutoConfig *autoConfig; Forbid(); if (autoConfig = (struct AutoConfig *)FindSemaphore("WORDS")) { ObtainSemaphore(&autoConfig->Semaphore); if (autoConfig->Users) --autoConfig->Users; ReleaseSemaphore(&autoConfig->Semaphore); if (autoConfig->Users == 0) { struct MsgPort *port; RemSemaphore (&autoConfig->Semaphore); ObtainSemaphore (&autoConfig->Semaphore); ReleaseSemaphore(&autoConfig->Semaphore); FreeVec(autoConfig); if (port = CreateMsgPort()) { if (autoConfig->Words) { struct Screen *screen; ULONG lock; // critical stuff (barely legal - don't do this at home, kids ;-) lock = LockIBase(0); // find frontmost screen screen = ((struct IntuitionBase *)IntuitionBase)->ActiveScreen; // keep system frozen Forbid(); // enable rendering functions UnlockIBase(lock); if (rtEZRequestTags("Save new words to user dictionary ?", "_SAVE|_cancel", NULL, NULL, RT_Underscore, '_', RTEZ_ReqTitle, "Words", RT_Screen, screen, TAG_DONE) == 1) SendRexxCommand("IRexxSpell", "ADD A", port, NULL); Permit(); } SendRexxCommand("IRexxSpell", "EXIT", port, NULL); DeleteMsgPort(port); } } } Permit(); } ///